/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const bitfield = Symbol('bitfieldSymbol');
const eventMap = Symbol('eventMapSymbol');
const kObjectMode: number = 1 << 0;
const kErrorEmitted: number = 1 << 1;
const kAutoDestroy: number = 1 << 2;
const kEmitClose: number = 1 << 3;
const kDestroyed: number = 1 << 4;
const kClosed: number = 1 << 5;
const kCloseEmitted: number = 1 << 6;
const kErrored: number = 1 << 7;
const kConstructed: number = 1 << 8;

function runNextTick(fn: Function, ...args: any[]) {
  Promise.resolve(args).then((args)=>fn(...args));
}

// foundation/ability/ability_runtime/frameworks/js/napi/app/context/context.js
class EventHub {
  [eventMap]: Map<string, Function[]> = new Map();

  constructor() {
  }

  on(event: string, callback: Function) {
    let res = this[eventMap].get(event);
    if (!res) {
      this[eventMap].set(event, [callback]);
    } else if (res.indexOf(callback) === -1) {
      res.push(callback);
    }
  }

  off(event: string, callback?: Function) {
    if (callback) {
      let res = this[eventMap].get(event);
      if (res) {
        let index = res.indexOf(callback);
        if (index > -1) {
          if (res.length == 1) {
            this[eventMap].delete(event);
          } else {
            for (; index + 1 < res.length; ++index) {
              res[index] = res[index + 1];
            }
            res.pop();
          }
        }
      }
    } else {
      this[eventMap].delete(event);
    }
  }

  emit(event: string, ...args: any[]) {
    let res = this[eventMap].get(event);
    if (res) {
      const len = res.length;
      for (let i = 0; i < len; ++i) {
        res[i].apply(this, args);
      }
    } else if (event === 'error') {
      if (args && args.length > 0) {
        let error = args[0];
        if (error instanceof Error) {
          throw error;
        }
      }
    }
  }

  listenerCount(event: string) : number {
    let res = this[eventMap].get(event);
    if (!res) {
      return 0;
    }
    return res.length;
  }
}

class Stream extends EventHub {
  constructor() {
    super();
  }
}

export {
  runNextTick,
  bitfield,
  // bitfields
  kObjectMode,
  kErrorEmitted,
  kAutoDestroy,
  kEmitClose,
  kDestroyed,
  kClosed,
  kCloseEmitted,
  kErrored,
  kConstructed,
  Stream
};